#include <iostream>
#include <boost/bind.hpp>
#include <tbb/scalable_allocator.h>

#include "TcpConnection.h"
#include "CustomAllocHandler.h"
#include "RequestHandler.h"
#include "RequestParser.h"
#include "WorkManager.h"


using namespace boost::asio;
using namespace std;
using namespace tbb;


TcpConnection::TcpConnection(io_service &ioService, WorkManager &manager)
	: _asyncOperationExpected(None),
      _bufferSize(0),
	  _manager(manager),
	  _socket(ioService)
{
	//
}

void *TcpConnection::operator new(size_t blockSize)
{
	return scalable_malloc(blockSize);
}

void TcpConnection::operator delete(void *pointer)
{
	scalable_free(pointer);
}

void TcpConnection::StartParsing()
{
	ip::tcp::no_delay noDelayOption(true);
	_socket.set_option(noDelayOption);

	socket_base::non_blocking_io command(true);
	_socket.io_control(command);

	socket_base::receive_buffer_size bufferSizeOption(512);
	_socket.set_option(bufferSizeOption);

	socket_base::linger lingerOption(false, 0);
	_socket.set_option(lingerOption);

	_socket.async_read_some(buffer(_buffer), MakeCustomAllocHandler(
		boost::bind(&TcpConnection::HandleRead, this, placeholders::error, placeholders::bytes_transferred)));
}

void TcpConnection::HandleRead(const boost::system::error_code &e, size_t bytesTransferred)
{
	if (e)
	{
		delete this;
		return;
	}

	_bufferSize = bytesTransferred;

	_asyncOperationExpected = Read;
	_manager.EnqueueWork(this, &TcpConnection::ParseReadAsync);
}

void TcpConnection::HandleWrite(const boost::system::error_code &e)
{
	if (e)
	{
		delete this;
		return;
	}

	_asyncOperationExpected = Write;
	_manager.EnqueueWork(this, &TcpConnection::ParseWriteAsync);
}

void TcpConnection::ParseReadAsync()
{
	if (_asyncOperationExpected != Read)
	{
		throw runtime_error("Possible queue underrun: TcpConnection received unexpected read callback");
	}

	_asyncOperationExpected = None;

	boost::tribool result;
    boost::tie(result, boost::tuples::ignore) = _requestParser.Parse(_request, _buffer.data(),
																		_buffer.data() + _bufferSize);

    if (result)
    {
		RequestHandler::HandleRequest(_request, _reply);

		async_write(_socket, _reply.ToBuffers(), MakeCustomAllocHandler(
			boost::bind(&TcpConnection::HandleWrite, this, placeholders::error)));
    }
    else if (!result)
    {
		_reply = Reply::StockReply(Reply::BadRequest);
		async_write(_socket, _reply.ToBuffers(), MakeCustomAllocHandler(
			boost::bind(&TcpConnection::HandleWrite, this, placeholders::error)));
    }
    else
    {
		_socket.async_read_some(buffer(_buffer), MakeCustomAllocHandler(
			boost::bind(&TcpConnection::HandleRead, this, placeholders::error, placeholders::bytes_transferred)));
    }
}

void TcpConnection::ParseWriteAsync()
{
	if (_asyncOperationExpected != Write)
	{
		throw runtime_error("Possible queue underrun: TcpConnection received unexpected write callback");
	}

	_asyncOperationExpected = None;

	// Initiate graceful connection closure.
	boost::system::error_code ignored_ec;
	_socket.shutdown(ip::tcp::socket::shutdown_both, ignored_ec);

	delete this; // [AM] usually, it is dangerous combination. So, it's another reason to separate parser out of tcpconnection. Not now however.
}